#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pcap.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ether.h>
#include <arpa/inet.h>
#include <time.h>
#include <unistd.h>

// ICMP校验和计算函数
unsigned short calculate_checksum(unsigned short *addr, int len)
{
    int nleft = len;
    int sum = 0;
    unsigned short *w = addr;
    unsigned short answer = 0;

    while (nleft > 1)
	{
        sum += *w++;
        nleft -= 2;
    }

    if (nleft == 1)
	{
        *(unsigned char *)(&answer) = *(unsigned char *)w;
        sum += answer;
    }

    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    answer = ~sum;
    return answer;
}

// ICMP协议分析器
void analyze_icmp_packet(const struct icmphdr *icmp, const u_char *payload, int payload_len)
{
    printf("\n=== ICMP协议分析 ===\n");
    
    // 分析ICMP头部
    printf("类型: %d (%s)\n", icmp->type, icmp->type == ICMP_ECHO ? "ECHO请求" : icmp->type == ICMP_ECHOREPLY ? "ECHO应答" : "其他");
    printf("代码: %d\n", icmp->code);
    printf("校验和: 0x%04x ", ntohs(icmp->checksum));
    
    // 验证校验和
    unsigned short received_checksum = icmp->checksum;
    struct icmphdr temp_icmp = *icmp;
    temp_icmp.checksum = 0;
    unsigned short calculated_checksum = calculate_checksum((unsigned short *)&temp_icmp, sizeof(struct icmphdr) + payload_len/2);
    
    if (received_checksum == calculated_checksum) printf("[校验和验证通过]\n");
    else printf("[校验和验证失败! 计算值:0x%04x]\n", calculated_checksum);
    
    printf("标识符: %d\n", ntohs(icmp->un.echo.id));
    printf("序列号: %d\n", ntohs(icmp->un.echo.sequence));
    
    // 分析数据部分
    printf("\n数据负载(%d字节):\n", payload_len);
    if (payload_len > 0)
	{
        // 打印ASCII内容
        printf("ASCII: ");
        for (int i = 0; i < (payload_len > 32 ? 32 : payload_len); i++)
		{
            if (isprint(payload[i])) putchar(payload[i]);
            else putchar('.');
        }
        printf("\n");
        
        // 打印HEX内容
        printf("HEX:   ");
        for (int i = 0; i < (payload_len > 16 ? 16 : payload_len); i++)
		{
            printf("%02x ", payload[i]);
            if ((i+1) % 8 == 0) printf(" ");
        }
        printf("\n");
    }
}

// 数据包处理器
void packet_handler(u_char *user_data, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
    struct ether_header *eth_header;
    struct ip *ip_header;
    struct icmphdr *icmp_header;
    char src_ip[INET_ADDRSTRLEN];
    char dst_ip[INET_ADDRSTRLEN];
    int ip_header_len;
    int icmp_payload_len;
    
    // 解析以太网头部
    eth_header = (struct ether_header *)packet;
    if (ntohs(eth_header->ether_type) != ETHERTYPE_IP) return; // 不是IP包，忽略
    
    // 解析IP头部
    ip_header = (struct ip *)(packet + sizeof(struct ether_header));
    ip_header_len = ip_header->ip_hl * 4;
    
    // 检查是否为ICMP协议
    if (ip_header->ip_p != IPPROTO_ICMP) return; // 不是ICMP包，忽略
    
    // 解析ICMP头部
    icmp_header = (struct icmphdr *)(packet + sizeof(struct ether_header) + ip_header_len);
    
    // 只处理ICMP ECHO请求
    if (icmp_header->type != ICMP_ECHO) return;
    
    // 计算ICMP负载长度
    icmp_payload_len = ntohs(ip_header->ip_len) - ip_header_len - sizeof(struct icmphdr);
    
    // 转换IP地址
    inet_ntop(AF_INET, &(ip_header->ip_src), src_ip, INET_ADDRSTRLEN);
    inet_ntop(AF_INET, &(ip_header->ip_dst), dst_ip, INET_ADDRSTRLEN);
    
    // 打印基本信息
    printf("\n捕获时间: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));
    printf("源IP: %s\n", src_ip);
    printf("目标IP: %s\n", dst_ip);
    printf("IP版本: %d\n", ip_header->ip_v);
    printf("IP头部长度: %d 字节\n", ip_header_len);
    printf("TTL: %d\n", ip_header->ip_ttl);
    printf("IP总长度: %d 字节\n", ntohs(ip_header->ip_len));
    
    // 进行ICMP协议分析
    analyze_icmp_packet(icmp_header, packet + sizeof(struct ether_header) + ip_header_len + sizeof(struct icmphdr), icmp_payload_len);
    
    printf("----------------------------------------\n");
}

int main(int argc, char *argv[])
{
    pcap_t *handle;
    char errbuf[PCAP_ERRBUF_SIZE];
    char *dev;
    struct bpf_program fp;
    char filter_exp[] = "icmp and icmp[0] == 8"; // 只捕获ICMP ECHO请求
    bpf_u_int32 net;
    int count = 0;
    int max_packets = -1; // 默认无限捕获
    
    // 解析命令行参数
    if (argc == 2) max_packets = atoi(argv[1]);
    
    // 查找网络设备
    dev = pcap_lookupdev(errbuf);
    if (dev == NULL)
	{
        fprintf(stderr, "找不到网络设备: %s\n", errbuf);
        return 1;
    }
    
    printf("使用网络设备: %s\n", dev);
    printf("过滤器: %s\n", filter_exp);
    
    // 打开网络设备
    handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
    if (handle == NULL)
	{
        fprintf(stderr, "无法打开设备 %s: %s\n", dev, errbuf);
        return 1;
    }
    
    // 检查链路层类型
    if (pcap_datalink(handle) != DLT_EN10MB)
	{
        fprintf(stderr, "设备 %s 不提供以太网头\n", dev);
        pcap_close(handle);
        return 1;
    }
    
    // 编译和设置过滤器
    if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1)
	{
        fprintf(stderr, "无法解析过滤器 %s: %s\n", filter_exp, pcap_geterr(handle));
        pcap_close(handle);
        return 1;
    }
    
    if (pcap_setfilter(handle, &fp) == -1)
	{
        fprintf(stderr, "无法安装过滤器 %s: %s\n", filter_exp, pcap_geterr(handle));
        pcap_close(handle);
        return 1;
    }
    
    printf("开始捕获并分析ICMP ECHO请求包...\n");
    printf("按Ctrl+C停止\n");
    
    // 开始捕获
    if (max_packets > 0) pcap_loop(handle, max_packets, packet_handler, (u_char *)&count);
    else pcap_loop(handle, -1, packet_handler, (u_char *)&count);
    
    // 清理资源
    pcap_freecode(&fp);
    pcap_close(handle);
    
    return 0;
}